/*
	MIDAS Context Based Routing
	cbr_packet.h
	Copyright (c) 2007 Radoslaw Olgierd Schoeneich
	MEAG IT PW
*/

#ifndef cbr_packet_h_
#define cbr_packet_h_

#include "packet.h"

#include "cbr_types.h"
#include "cbr_triple.h"

// packet types
#define CBRTYPE_HELLO	0x01
#define CBRTYPE_RREQ1	0x02
#define CBRTYPE_RREQ2	0x03
#define CBRTYPE_RREP	0x04
#define CBRTYPE_DATA	0x06
#define CBRTYPE_MASK	0x0f

#define CBRTYPE_MCAST	0x20	// for RREQ1/RREQ2 only

/**
 * \struct hdr_cbr common CBR header included in all CBR packets
 * \note sizeof(struct hdr_cbr) will return 12 because:
 * - the structure uses packed attribute
 * - sizeof() ignores static members
 */

struct hdr_cbr {
	/** packet type and flags
	    (four most significant bits carry flag bits) */
	u_int8_t	type;

	/** CBR packet length, including common header
	    (only for Network Simulator) */
	u_int16_t	size;

	/** sender node ID */
	u_int32_t	src;

	/** retransmission counter
	     (devel code, not for MIDAS project) */
	int8_t		retr_count;

	/** previous node (forwarder) ID (unused) */
	u_int32_t	prev_hop;

	static int offset_;
	inline static int& offset() { return offset_; }
	inline static hdr_cbr* access(const Packet* p)
	{
		return (hdr_cbr *) p->access(offset_);
	}
} __attribute__ ((packed));

class cbr_Packet : private cbr_Object {
protected:
	cbr_ID			src;
	cbr_ID			dest;	// in RREP, RERR and DATA
	Packet*			pkt_;	// the inner NS packet
	u_int8_t		type;

public:
	static int32_t		maxSize;

	cbr_Packet() { }
	virtual ~cbr_Packet() { };

	virtual int32_t		getSize() const = 0;
	virtual int32_t		getControl() const = 0;
	virtual int32_t		getData() const = 0;

	cbr_ID			getSource() const { return src; }
	cbr_ID			getDestination() const { return dest; }
	u_int8_t		getType() const { return type; }
	Packet* 		pkt() const { return pkt_; }
};

class cbr_HelloPacket : public cbr_Packet {
protected:
	cbr_TriplesList		internal;
	cbr_TriplesList		external;
	int32_t			size;

public:
	cbr_HelloPacket(cbr_ID src, const cbr_TriplesList& list);
	cbr_HelloPacket(Packet *pkt);
	~cbr_HelloPacket();

	virtual int32_t		getSize() const { return size; }
	virtual int32_t		getControl() const { return size; }
	virtual int32_t		getData() const { return 0; }
	virtual void		getTriples(cbr_TriplesList& internal,
					   cbr_TriplesList& external) const;
};

class cbr_Msg;
class cbr_ContextAddress;

class cbr_RReqPacket : public cbr_Packet {
protected:
	int32_t		size;
	bool		mcast;

	u_int32_t	transactionID;

	int32_t		payloadLength;
	int32_t		totalLength;
	char*		payload;

	cbr_ContextAddress *addr;

public:
	cbr_RReqPacket(cbr_ID src, cbr_Msg* msg, bool mcast);
	cbr_RReqPacket(Packet *pkt);
	~cbr_RReqPacket();

	virtual int32_t	getSize() const { return size; }
	virtual int32_t getControl() const { return size - payloadLength; }
	virtual int32_t getData() const { return payloadLength; }

	u_int32_t	getTransactionID() const { return this->transactionID;}
	const cbr_ContextAddress* getAddress() const { return this->addr; }
	bool		isMulticast() const { return this->mcast; }

	int32_t 	getTotalLength() const { return this->totalLength; }
	int32_t 	getPayloadLength() const { return this->payloadLength;}
	const char* 	getPayload() const { return this->payload; }
};

class cbr_RRepPacket : public cbr_Packet {
protected:
	int32_t		size;
	u_int32_t	transactionID;

public:
	cbr_RRepPacket(cbr_ID src, cbr_ID dest, u_int32_t trID);
	cbr_RRepPacket(Packet *pkt);

	virtual int32_t	getSize() const { return size; }
	virtual int32_t getControl() const { return size; }
	virtual int32_t getData() const { return 0; }

	u_int32_t	getTransactionID() const { return this->transactionID;}
};

class cbr_DataPacket : public cbr_Packet {
protected:
	int32_t		size;

	u_int32_t	transactionID;

	int32_t		payloadLength;
	int32_t		offset;
	char*		payload;

public:
	static int32_t	maxPayload;

	cbr_DataPacket(cbr_ID src, cbr_ID dest, u_int32_t trID,
		       const char *payload, int32_t offs, int32_t length);
	cbr_DataPacket(Packet *pkt);
	~cbr_DataPacket();

	virtual int32_t	getSize() const { return size; }
	virtual int32_t getControl() const { return size - payloadLength; }
	virtual int32_t getData() const { return payloadLength; }

	u_int32_t	getTransactionID() const { return this->transactionID;}
	int32_t 	getOffset() const { return this->offset; }
	int32_t 	getPayloadLength() const { return this->payloadLength;}
	const char* 	getPayload() const { return this->payload; }
};

/**
 * Decide whether to log CBR packet.
 *
 * @param type CBR packet type
 *
 * @return 'true' if CBR packet should be logged, 'false' otherwise.
 */

static inline bool cbr_log_cbr_packet(u_int8_t type)
{
	switch (type & CBRTYPE_MASK) {
	case CBRTYPE_HELLO:
		if (log_packets & CBR_LOG_PACKETS_CBR_HELLO)
			return 1;
	case CBRTYPE_RREQ1:
	case CBRTYPE_RREQ2:
	case CBRTYPE_RREP:
		if (log_packets & CBR_LOG_PACKETS_CBR_ROUTE)
			return 1;
		break;
	case CBRTYPE_DATA:
		if (log_packets & CBR_LOG_PACKETS_CBR_DATA)
			return 1;
	}

	return 0;
}

#endif	// cbr_packet_h_
